﻿using UnityEngine;

namespace GameToolkit
{
    [System.Flags]
    public enum ECullingUpdateMode //: byte
    {
        Update = CullingCamera.ID_UPDATE,
        FixedUpdate = CullingCamera.ID_FIXED_UPDATE,
        Background = CullingCamera.ID_FAR_BACKGROUD,
        LateUpdate = CullingCamera.ID_LATE_UPDATE,
    }

    public enum ECullDistance
    {
        Off,
        NearBy,
        Middle,
        Far,
        VeryFar,
        Infinite,
    }

    public interface ICullingTag
    {
    }

    public interface ICullingAction : ICullingTag
    {
        void OnCulling(CullingCamera cam, bool visible, int distancelv);
    }

    public interface ICullingComponent : ICullingTag
    {
        void OnCullingEnabled(CullingCamera culler, ICullingBounds target);
        void OnCullingDisabled();
    }

    public interface ICullingUpdate : ICullingTag
    {
        /// <summary>
        /// 视锥更新距离
        /// </summary>
        int CulledUpdateDistanceLv { get; }
        /// <summary>
        /// 持续更新距离
        /// </summary>
        int AlwaysUpdateDistanceLv { get; }
        ECullingUpdateMode UpdateMode { get; }
        void OnCullingUpdate(CullingCamera culler, ECullingUpdateMode mode);
    }

    /// <summary>
    /// 裁剪系统回调事件
    /// </summary>
    public interface ICullEvent
    {
        int Layer { get; }
    }

    /// <summary>
    /// OnPreCull callback
    /// </summary>
    public interface IPreCullAction : ICullEvent
    {
        void OnPreCullAction(Camera camera);
    }

    /// <summary>
    /// 裁剪更新前回调
    /// </summary>
    public interface IBeforeCullingUpdate : ICullEvent
    {
        //ECullingUpdateMode UpdateMode { get; }
        void OnBeforeCullingUpdate(Camera camera);
    }

    /// <summary>
    /// 裁剪更新后回调
    /// </summary>
    public interface IAfterCullingUpdate : ICullEvent
    {
        //ECullingUpdateMode UpdateMode { get; }
        void OnAfterCullingUpdate(Camera camera);
    }

    public struct CullingHandle : System.IEquatable<CullingHandle>
    {
        CullingCamera _camera;
        public CullingCamera camera { get { return _camera; } }
        int _cullingIndex;
        public int cullingIndex { get { return _cullingIndex; } }
        ICullingBounds _bounds;
        public ICullingBounds bounds { get { return _bounds; } }

        internal CullingHandle(CullingCamera camera, int index, ICullingBounds bounds)
        {
            this._camera = camera;
            this._cullingIndex = index;
            this._bounds = bounds;
        }

        public void Rebounding()
        {
            if (camera != null)
            {
                camera.Recalculate(cullingIndex);
            }
        }

        public void Release()
        {
            var cam = _camera;
            _camera = null;
            if(cam != null)
            {
                cam.RemoveCullingTarget(_cullingIndex, _bounds);
            }
        }

        public bool Equals(CullingHandle other)
        {
            return camera == other.camera && cullingIndex == other.cullingIndex;
        }

    }

    public struct CullingState
    {
        public CullingCamera camera;
        public int distanceLv;
        public bool visible;
        public ICullingBounds bounds;
        public bool updating;
        public bool background;

#if UNITY_EDITOR
        //public bool raycast;
        //public bool highlight;
        public UnityEditor.GizmoType type;
        public Color color
        {
            get
            {
                Color c;
                if (camera != null && visible)
                {
                    c = CullingCamera.colors[distanceLv];
                }
                else
                {
                    c = /*Application.isPlaying ?*/ Color.gray /*: Color.cyan*/;
                }
                if (Application.isPlaying && (updating || background))
                {
                    c *= Mathf.PingPong((updating ? 4f : 1f) * Time.realtimeSinceStartup, 1f) * (updating ? 1.4f : 1f) + 0.5f;
                }
                if ((type & UnityEditor.GizmoType.Pickable) == 0)
                    c *= 0.5f;
                return c;
            }
        }
#endif
    }

    /// <summary>
    /// 裁剪组bounds
    /// </summary>
    public interface ICullingBounds : IValidator
    {
        int Layer { get; }
        int TagCount { get; }
        BoundingSphere Bounding { get; }
        ICullingTag GetCullingTag(int index);
        bool HandleCullingCamera(CullingHandle handler);
        void ValidateCullingState(CullingCamera culler, bool visible, int distanceLv);
        void ReleaseCullingCamera(CullingCamera cam);
    }

    public interface ICullingBoundsDebug
    {
        string Title { get; }
        void Select();
        void DrawGizmos(CullingState state);
        bool Raycast(Ray ray, out Vector3 point);
    }

#if UNITY_EDITOR
    public interface IPrefabStageInstance
    {
        bool IsPrefabStage { get; }
    }

#endif

    public struct RaycastArea
    {
        public Transform transform;
        public Bounds localBounds;
        public bool isSphere;
        public RaycastArea(Transform trans, Bounds bounds, bool isSphere)
        {
            this.transform = trans;
            this.localBounds = bounds;
            this.isSphere = isSphere;
        }

    }
}